package net.mof.manual

import com.squareup.javapoet.*
import net.mof.manual.Utils.JSONExceptionClassName
import net.mof.manual.Utils.JSONObject_ClassName
import net.mof.manual.Utils.jsonObject
import net.mof.manual.Utils.manualClassName
import net.mof.manual.Utils.stringClassName
import net.mof.manual.Utils.textUtilsClassName
import net.mof.manual.meta.BaseFieldMetaInfo
import net.mof.manual.meta.MapFieldMetaInfo
import javax.lang.model.element.Modifier

/**
 *
 * 解析被@JsonMark标记的类
 *
 * Created by LiYueHui on 2020/2/2.
 */
class MarkedClassBrew {

    fun createTypeToJSONObject(markedClass: MarkedClass,
                               thisClassName: ClassName?): MethodSpec? {
        val param = "__type__"
        val firstCode = CodeBlock
                .builder()
                .addStatement("\$T \$L = new \$T()", JSONObject_ClassName,
                        jsonObject, JSONObject_ClassName)
                .beginControlFlow("if(\$L != null)", param)
                .add(createTypeToJsonObjectPut(markedClass, param)).build()
        val returnCode = CodeBlock.builder().endControlFlow()
                .addStatement("return \$L", jsonObject).build()
        return MethodSpec.methodBuilder("typeToJSONObject")
                .addModifiers(Modifier.PUBLIC).addAnnotation(Override::class.java)
                .addCode(firstCode).addCode(returnCode)
                .returns(JSONObject_ClassName)
                .addParameter(thisClassName, param).build()
    }

    private fun createTypeToJsonObjectPut(markedClass: MarkedClass,
                                          param: String): CodeBlock? {
        val builder = CodeBlock.builder()
        var list = ArrayList<BaseFieldMetaInfo>()
        baseFileList(markedClass.superMarkedClass, list)
        list?.forEach { metaInfoBase: BaseFieldMetaInfo? ->
            if (metaInfoBase !is MapFieldMetaInfo) {
                putField(metaInfoBase, builder, param)
            }
        }
        markedClass.baseFieldList?.forEach { metaInfoBase: BaseFieldMetaInfo? ->
            putField(metaInfoBase, builder,
                    param)
        }

        return builder.build()
    }

    private fun baseFileList(markedClass: MarkedClass?, list: MutableList<BaseFieldMetaInfo>) {
        if (markedClass?.baseFieldList != null && !markedClass?.baseFieldList.isEmpty()) {
            list.addAll(markedClass.baseFieldList)
            baseFileList(markedClass.superMarkedClass, list)
        }
    }

    private fun putField(metaInfoBase: BaseFieldMetaInfo?, builder: CodeBlock.Builder,
                         param: String) {
        builder.beginControlFlow("try")
        metaInfoBase?.typeToJsonObjectPut(builder, param)
        builder.endControlFlow()
        builder.beginControlFlow("catch (\$L e)", JSONExceptionClassName)
                .addStatement("e.printStackTrace()").endControlFlow()
    }

    fun createJsonToType(markedClass: MarkedClass,
                         thisClassName: ClassName?): MethodSpec? {
        val param = "__input__"
        val classNameBean = markedClass.markedClassInfo
        val firstCode = CodeBlock
                .builder()
                .addStatement("\$L \$L = new \$L()", classNameBean?.getClassName(),
                        classNameBean?.getFileName()?.toLowerCase(),
                        classNameBean?.getClassName())
                .beginControlFlow("if(!\$T.isEmpty(\$L))", textUtilsClassName,
                        param).beginControlFlow("try")
                .add(createJsonToTypeGet(markedClass, param)).endControlFlow()
                .beginControlFlow("catch (\$L e)", JSONExceptionClassName)
                .addStatement("e.printStackTrace()").endControlFlow()
                .endControlFlow().build()
        val returnCode = CodeBlock
                .builder()
                .addStatement("return \$L",
                        classNameBean?.getFileName()?.toLowerCase()).build()
        return MethodSpec.methodBuilder("jsonToType")
                .addModifiers(Modifier.PUBLIC)
                .addParameter(stringClassName, param)
                .addAnnotation(Override::class.java).addCode(firstCode)
                .addCode(returnCode).returns(thisClassName).build()
    }

    private fun createJsonToTypeGet(markedClass: MarkedClass, param: String): CodeBlock? {
        val builder = CodeBlock.builder()
        val classNameBean = markedClass.markedClassInfo
        builder.addStatement("\$T \$L = new \$T(\$L)", JSONObject_ClassName,
                jsonObject, JSONObject_ClassName, param)
        // normal
        if (markedClass.subTypeMap.isEmpty()) {

            var list = ArrayList<BaseFieldMetaInfo>()
            baseFileList(markedClass.superMarkedClass, list)
            list?.forEach { metaInfoBase: BaseFieldMetaInfo? ->
                if (metaInfoBase is MapFieldMetaInfo) {
                    (metaInfoBase as MapFieldMetaInfo?)?.baseFieldList = null
                }
                metaInfoBase?.jsonToTypeGetField(builder, classNameBean!!)
            }

            markedClass.baseFieldList.forEach { metaInfoBase: BaseFieldMetaInfo? ->
                metaInfoBase?.jsonToTypeGetField(
                        builder, classNameBean!!)
            }
            // subType
        } else {
            val key = "map_key"
            val aClass = "aClass"
            val wildcard = WildcardTypeName.subtypeOf(ClassName.get(markedClass.packageName,
                    markedClass.markedClassInfo?.getClassName()));
            val mapClass =
                    ParameterizedTypeName.get(ClassName.get(Class::class.java), wildcard)
            builder.addStatement("\$T \$L = \$L.optString(\$S)", stringClassName,
                    key, jsonObject, "type")
            builder.addStatement("\$T \$L = \$L.get(\$L)", mapClass, aClass,
                    MarkedClass.field_map, key)
            builder.beginControlFlow(
                    "if (\$L != null && \$L != \$T.class)",
                    aClass,
                    aClass,
                    ClassName.get(markedClass.packageName,
                            classNameBean!!.getClassName()))
            builder.addStatement("return \$T.jsonToType(\$L,\$L)",
                    manualClassName, param, aClass)
            builder.endControlFlow()
            builder.beginControlFlow("else")
            markedClass.baseFieldList
                    .forEach { metaInfoBase: BaseFieldMetaInfo? ->
                        if (metaInfoBase is MapFieldMetaInfo) {
                            (metaInfoBase as MapFieldMetaInfo?)?.baseFieldList = markedClass.baseFieldList
                        }
                        metaInfoBase!!.jsonToTypeGetField(builder, classNameBean)
                    }
            builder.endControlFlow()
        }
        return builder.build()
    }
}